package com.sec.android.app.myfiles.thumbnail;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.LruCache;
import android.widget.ImageView;

import com.samsung.android.sdk.slinkcloud.CloudGatewayThumbnailUtils;
import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.info.FileType;
import com.sec.android.app.myfiles.info.MediaFile;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.abstraction.FileRecord.StorageType;
import com.sec.android.app.myfiles.module.cloud.CloudFileRecord;
import com.sec.android.app.myfiles.module.trash.TrashFileRecord;
import com.sec.android.app.myfiles.util.JUnitHandler;
import com.sec.android.app.myfiles.util.PreferenceUtils;
import com.sec.android.app.myfiles.util.chain.ChainBuilder;

import java.lang.ref.WeakReference;

public class ThumbnailMgr {
    private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() / 2;

    // Default thumbnail size
    private static int sDefaultThumbnailSizeShortcut;

    private Context mContext;

    private HandlerThread[] mThumbnailThread;
    private ThumbnailHandler[] mThumbnailHandler;
    private int mCurThreadIndex = 0;

    private ThumbnailImp mThumbnailImp;

    private static final String THUMBNAIL_THREAD_NAME = "thumbnail_thread";

    private static ThumbnailMgr sInstance;
    private FileCacheMgr mFileCacheMgr = null;
    private MemoryCacheMgr mMemoryCacheMgr = null;
    private static final int MAX_FAIL_LIST = 1024;
    private LruCache<Integer, String> mFailedCache = new LruCache<>(MAX_FAIL_LIST);
    private Handler mUiUpdateHandler = new WeakRefHandler();

    public static ThumbnailMgr getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new ThumbnailMgr(context.getApplicationContext());
        }

        return sInstance;
    }

    private ThumbnailMgr(Context context) {
        mContext = context;
        mFileCacheMgr = FileCacheMgr.createFileCacheMgr(context);
        mMemoryCacheMgr = MemoryCacheMgr.getInstance();

        Resources r = context.getResources();
        sDefaultThumbnailSizeShortcut = r.getDimensionPixelSize(R.dimen.shortcut_icon_thumbnail_size);

        ChainBuilder<ThumbnailImp> builder = new ChainBuilder<>().
                append(new ImageThumbnailImp(mContext)).
                append(new VideoThumbnailImp(mContext)).
                append(new AudioThumbnailImp(mContext)).
                append(new ApkThumbnailImp(mContext));
        mThumbnailImp = builder.getInstance();

        mThumbnailThread = new HandlerThread[MAX_THREAD];
        mThumbnailHandler = new ThumbnailHandler[MAX_THREAD];

        for (int i = 0; i < MAX_THREAD; i++) {
            mThumbnailThread[i] = new HandlerThread(THUMBNAIL_THREAD_NAME + i, Process.THREAD_PRIORITY_BACKGROUND);
            mThumbnailThread[i].start();

            Looper looper = mThumbnailThread[i].getLooper();

            if (looper != null) {
                mThumbnailHandler[i] = new ThumbnailHandler(looper);
            }
        }
        if (isThemeChange()) {
            mFileCacheMgr.clearDB();
        }
    }

    private boolean isThemeChange() {
        String previousTheme;
        String currentTheme;
        boolean isThemeChanged;
        currentTheme = Settings.System.getString(mContext.getContentResolver(), "current_sec_active_themepackage");

        previousTheme = PreferenceUtils.getPreviousTheme(mContext);

        if (previousTheme == null && currentTheme == null) {
            isThemeChanged = false;
        } else if (previousTheme == null || currentTheme == null) {
            isThemeChanged = true;
        } else {
            isThemeChanged = currentTheme.equals(previousTheme);
        }

        PreferenceUtils.setPreviousTheme(mContext, currentTheme);

        return isThemeChanged;
    }

    private boolean skipDeletedDownloadFile(FileRecord record) {
        boolean bRet = false;

        if (record.getStorageType() == StorageType.Downloads) {
            if (!record.exists(mContext)) {
                bRet = true;
            }
        }
        return bRet;
    }


    /**
     * <pre>
     * Load thumbnail to image view
     * </pre>
     *
     * @return updated imageView (null returned if load background)
     */
    public ImageView loadThumbnail(FileRecord record, ImageView thumbnailView, ImageView iconView) {
        if (thumbnailView == null || iconView == null) {
            return null;
        }

        ImageView ret = null;

        thumbnailView.setTag(record);
        iconView.setTag(record);

        if (mThumbnailImp.isSupportType(record) && record.isValidFullPath() && !skipDeletedDownloadFile(record) && !isFailed(record)) {
            ImageView imageView = getThumbnailView(record, thumbnailView, iconView);

            Bitmap bmp = mMemoryCacheMgr.getCache(record);

            if (imageView != null) {
                if (bmp != null) {
                    imageView.setImageBitmap(bmp);
                    ret = imageView;
                } else {
                    imageView.setImageBitmap(null);

                    ThumbnailReqInfo info = new ThumbnailReqInfo(record, thumbnailView, iconView);
                    mThumbnailHandler[mCurThreadIndex].sendMessageAtFrontOfQueue(mThumbnailHandler[mCurThreadIndex].obtainMessage(0, info));

                    mCurThreadIndex++;
                    if (mCurThreadIndex >= MAX_THREAD) {
                        mCurThreadIndex = 0;
                    }
                }
            }
        } else {
            thumbnailView.setImageBitmap(null);
            iconView.setImageBitmap(null);
        }
        return ret;
    }

    public void clearCache() {
        mMemoryCacheMgr.clearCache();
        mFailedCache.evictAll();
    }

    private boolean isFailed(FileRecord record) {
        boolean bRet = false;
        String keyValue = mFailedCache.get(record.hashCode());
        if (keyValue != null) {
            bRet = keyValue.equals(record.getKey());
        }
        return bRet;
    }

    private void addToFailed(FileRecord record) {
        if (record.getStorageType() != StorageType.Cloud) {
            mFailedCache.put(record.hashCode(), record.getKey());
        }
    }

    private Bitmap getCloudThumbnail(FileRecord record) {
        Bitmap ret = null;
        int mediaType = -1;
        if (record.isImageFileType()) {
            mediaType = MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
        } else if (record.isVideoFileType()) {
            mediaType = MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
        }

        int id = record.getId();
        if ((mediaType > 0) && (id >= 0)) {
            Options options = new Options();
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            int deviceId;

            if (record.getStorageType() == StorageType.Cloud) {
                deviceId = ((CloudFileRecord) record).getDeviceId();
            } else {
                deviceId = ((TrashFileRecord) record).getDeviceId();
            }

            Bitmap bitmap = CloudGatewayThumbnailUtils.getThumbnailBitmap(mContext.getContentResolver(), mediaType, deviceId, id, 10,
                    320, 320, false, options);
            if (bitmap != null) {
                ret = ThumbnailUtils.extractThumbnail(bitmap, ThumbnailImp.THUMBNAIL_SIZE, ThumbnailImp.THUMBNAIL_SIZE);
            }
        }
        return ret;
    }

    public Bitmap getHomeShortcutBitmap(Context context, int width, FileRecord record) {
        Bitmap thumbnail;
        int originWidth;
        int originHeight;
        int longerLineLength;
        boolean isFileTypeIcon = false;

        thumbnail = mThumbnailImp.createThumbnail(record, sDefaultThumbnailSizeShortcut);
        // TODO need refactoring. below code is to get default thumbnail.
        // what is better the place of below code in here or
        // DefaultThumbnailImp?
        Resources r = context.getResources();
        if (thumbnail == null) {
            if (record.isDirectory()) {
                thumbnail = BitmapFactory.decodeResource(r, R.drawable.myfiles_thumb_folder_home);
            } else {
                BitmapDrawable drawable = (BitmapDrawable) MediaFile.getFileTypeDrawable(context, record);
                if (drawable != null) {
                    thumbnail = drawable.getBitmap();
                }
            }
            isFileTypeIcon = true;
        }

        if (thumbnail != null) {
            originWidth = thumbnail.getWidth();
            originHeight = thumbnail.getHeight();
            longerLineLength = Math.max(originWidth, originHeight);
            float scaleRatio = (float) width / (float) longerLineLength;

            // TODO
            // Below code consider the square shape icon.
            // It should be considered all shape of rectangle icon.

            // bitmap size of icon is different with target icon size. scale
            // up
            // or down the bitmap.
            if (width != longerLineLength && scaleRatio > 0.0f && originWidth > 0 && originHeight > 0) {
                thumbnail = Bitmap.createScaledBitmap(thumbnail, (int) (originWidth * scaleRatio), (int) (originHeight * scaleRatio), true);
            }

            if (record.isVideoFileType() && !isFileTypeIcon) {
                try {
                    Bitmap newBitmap = Bitmap.createBitmap(thumbnail.getWidth(), thumbnail.getHeight(), thumbnail.getConfig());
                    Canvas canvas = new Canvas(newBitmap);
                    canvas.drawBitmap(thumbnail, 0, 0, null);
                    Drawable playIcon = r.getDrawable(R.drawable.myfiles_list_btn_play_shortcut, null);
                    int playIconSize = r.getDimensionPixelSize(R.dimen.shortcut_play_icon_size);
                    int horizontalSpacing = (thumbnail.getWidth() - playIconSize) / 2;
                    int verticalSpacing = (thumbnail.getHeight() - playIconSize) / 2;
                    playIcon.setBounds(horizontalSpacing, verticalSpacing, thumbnail.getWidth() - horizontalSpacing, thumbnail.getHeight() - verticalSpacing);
                    playIcon.draw(canvas);
                    thumbnail.recycle();
                    thumbnail = newBitmap;
                } catch (NullPointerException e) {
                    Log.e(this, "NullPointerException:" + e.toString());
                }
            }
        }

        return thumbnail;
    }

    private final class ThumbnailHandler extends JUnitHandler<ThumbnailReqInfo> {
        public ThumbnailHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            ThumbnailReqInfo reqInfo = getMsgValue(msg.obj);

            if (reqInfo != null) {
                reqInfo.mBmp = mFileCacheMgr.getCache(reqInfo.mRecord);

                if (reqInfo.mBmp == null) {
                    /**
                     * Check request file is same as real file
                     */
                    if (reqInfo.mRecord.getStorageType() == StorageType.Cloud
                            || reqInfo.mRecord.getStorageType() == StorageType.Trash) {
                        reqInfo.mBmp = getCloudThumbnail(reqInfo.mRecord);
                    } else {
                        reqInfo.mBmp = mThumbnailImp.createThumbnail(reqInfo.mRecord);
                    }

                    if (reqInfo.mBmp != null) {
                        mFileCacheMgr.addCache(reqInfo.mRecord, reqInfo.mBmp);
                    }
                }

                if (reqInfo.mBmp != null) {
                    mUiUpdateHandler.sendMessageAtFrontOfQueue(mUiUpdateHandler.obtainMessage(0, reqInfo));
                    mMemoryCacheMgr.addCache(reqInfo.mRecord, reqInfo.mBmp);
                } else {
                    addToFailed(reqInfo.mRecord);
                }
            }

        }
    }

    private static class WeakRefHandler extends Handler {
        public void handleMessage(Message msg) {
            if (msg != null) {
                ThumbnailReqInfo reqInfo = (ThumbnailReqInfo) msg.obj;
                if (reqInfo != null) {
                    ImageView imageView = reqInfo.getThumbnailView();
                    if (imageView != null) {
                        FileRecord tag = (FileRecord) imageView.getTag();
                        if (tag != null && tag.getFullPath().equals(reqInfo.mRecord.getFullPath())) {
                            imageView.setImageBitmap(reqInfo.mBmp);
                        }
                    }
                }
            }
        }
    }

    private static ImageView getThumbnailView(FileRecord record, ImageView thumbnailView, ImageView iconView) {
        ImageView ret = null;
        if (record != null) {
            ImageView otherView;
            if (FileType.isInstallFileType(record.getFileType())) {
                ret = iconView;
                otherView = thumbnailView;
            } else {
                ret = thumbnailView;
                otherView = iconView;
            }

            FileRecord tag = (FileRecord) ret.getTag();
            if (tag != null && tag.getFullPath().equals(record.getFullPath())) {
                if (otherView != null) {
                    otherView.setImageBitmap(null);
                }
            }
        }

        return ret;
    }

    private static class ThumbnailReqInfo {
        private FileRecord mRecord;
        private WeakReference<ImageView> mThumbnailViewWeak;
        private WeakReference<ImageView> mIconViewWeak;

        private Bitmap mBmp;

        private ThumbnailReqInfo(FileRecord record, ImageView thumbnailView, ImageView iconView) {
            mRecord = record;
            mThumbnailViewWeak = new WeakReference<>(thumbnailView);
            mIconViewWeak = new WeakReference<>(iconView);
        }

        private ImageView getThumbnailView() {
            ImageView ret = null;
            ImageView thumbnailView = mThumbnailViewWeak.get();
            ImageView iconView = mIconViewWeak.get();
            if (thumbnailView != null && iconView != null) {
                ret = ThumbnailMgr.getThumbnailView(mRecord, thumbnailView, iconView);
            }
            return ret;
        }
    }
}
